#include "model.h"
#include <iostream>
#include <vector>
using namespace std;

void Model::draw() {

    glPushMatrix();

    glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);

    glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, shininess);

    glTranslatef(translate[0], translate[1], translate[2]);
    glRotatef(angle, rotate[0], rotate[1], rotate[2]);
    glScalef(scale[0], scale[1], scale[2]);

    drawCoordinates();

    glPopMatrix();
}

/*-----------function for material setting-----------*/
void Model::setAmbient(float r, float g, float b, float a) {
    ambient[0] = r;
    ambient[1] = g;
    ambient[2] = b;
    ambient[3] = a;
}

void Model::getAmbient(float &r, float &g, float &b, float &a) {
    r = ambient[0];
    g = ambient[1];
    b = ambient[2];
    a = ambient[3];
}

void Model::setDiffuse(float r, float g, float b, float a) {
    diffuse[0] = r;
    diffuse[1] = g;
    diffuse[2] = b;
    diffuse[3] = a;
}

void Model::getDiffuse(float &r, float &g, float &b, float &a) {
    r = diffuse[0];
    g = diffuse[1];
    b = diffuse[2];
    a = diffuse[3];
}

void Model::setSpecular(float r, float g, float b, float a) {
    specular[0] = r;
    specular[1] = g;
    specular[2] = b;
    specular[3] = a;
}

void Model::getSpecular(float &r, float &g, float &b, float &a) {
    r = specular[0];
    g = specular[1];
    b = specular[2];
    a = specular[3];
}

void Model::setShininess(float s) { shininess[0] = s; }

void Model::getShininess(float &s) { s = shininess[0]; }

/*-----------function for translate/scale/rotate setting-----------*/
void Model::setTranslate(float x, float y, float z) {
    translate[0] = x;
    translate[1] = y;
    translate[2] = z;
    modifyBoundingbox();
}

void Model::getTranslate(float &x, float &y, float &z) {
    x = translate[0];
    y = translate[1];
    z = translate[2];
}

void Model::setScale(float x, float y, float z) {
    scale[0] = x;
    scale[1] = y;
    scale[2] = z;
    modifyBoundingbox();
}

void Model::getScale(float &x, float &y, float &z) {
    x = scale[0];
    y = scale[1];
    z = scale[2];
}

void Model::setRotate(float angle, float x, float y, float z) {
    rotate[0] = x;
    rotate[1] = y;
    rotate[2] = z;
    this->angle = angle;
    modifyBoundingbox();
}

void Model::getRotate(float &angle, float &x, float &y, float &z) {
    x = rotate[0];
    y = rotate[1];
    z = rotate[2];
    angle = this->angle;
}

/*-----------function for texture setting-----------*/
/*
void setTexture(const std::string &texname){
    this->texname.assign(texname);
}

void resetTexture(){
    this->texname.assign("");
}

std::string getTexture(){
    return this->texname;
}
*/

void Model::setTexture(unsigned texid) { this->texid = texid; }

void Model::resetTexture() { this->texid = 0; }

unsigned Model::getTexture() { return this->texid; }

void Model::getObject(vector<vector<GLfloat>> &vSets,
                      vector<vector<GLfloat>> &vtSets,
                      vector<vector<GLfloat>> &vnSets,
                      vector<vector<GLint>> &fSets) {}

void Model::modifyBoundingbox() {
    // cout << "modify" << endl;
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 3; j++) {
            newboundingbox[i][j] = boundingbox[i][j];
        }
    }

    // first, calculate the bounding box
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 3; j++) {
            newboundingbox[i][j] *= scale[j];
        }
    }

    // if (rotate[0] > 0 || rotate[1] > 0 && rotate[2] > 0) {
    double l =
        sqrt(pow(rotate[0], 2.0) + pow(rotate[1], 2.0) + pow(rotate[2], 2.0));
    double ux = rotate[0] / l;
    double uy = rotate[1] / l;
    double uz = rotate[2] / l;
    for (int i = 0; i < 8; i++) {
        rotatePoint(ux, uy, uz, angle, newboundingbox[i][0],
                    newboundingbox[i][1], newboundingbox[i][2]);
    }
    //}

    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 3; j++) {
            newboundingbox[i][j] += translate[j];
        }
    }

    // second, calculate the edge
    edge[0][0] = newboundingbox[0][0];
    edge[0][1] = newboundingbox[0][0];
    edge[1][0] = newboundingbox[0][1];
    edge[1][1] = newboundingbox[0][1];
    edge[2][0] = newboundingbox[0][2];
    edge[2][1] = newboundingbox[0][2];
    for (int i = 1; i < 8; i++) {
        for (int j = 0; j < 3; j++) {
            if (newboundingbox[i][j] < edge[j][0]) {
                edge[j][0] = newboundingbox[i][j];
            } else if (newboundingbox[i][j] > edge[j][1]) {
                edge[j][1] = newboundingbox[i][j];
            }
        }
    }
}

bool Model::isHit(float x, float y, float z) {
    y = -y;
    cout << x << " " << y << " " << z << endl;
    cout << edge[0][0] << " " << edge[0][1] << " " << edge[1][0] << " "
         << edge[1][1] << " " << edge[2][0] << " " << edge[2][1] << endl;
    // z = -z;
    if (x > edge[0][0] && x < edge[0][1] && y > edge[1][0] && y < edge[1][1] &&
        z > edge[2][0] && z < edge[2][1]) {
        return true;
    } else {
        return false;
    }
}

void rotatePoint(float ux, float uy, float uz, float angle, float &x, float &y,
                 float &z) {
    float newx, newy, newz;

    float matrix[4][4];

    float cosa = cos(angle);
    float sina = sin(angle);

    matrix[0][0] = ux * ux * (1 - cosa) + cosa;
    matrix[1][0] = uy * ux * (1 - cosa) + uz * sina;
    matrix[2][0] = uz * ux * (1 - cosa) - uy * sina;

    matrix[0][1] = ux * uy * (1 - cosa) - uz * sina;
    matrix[1][1] = uy * uy * (1 - cosa) + cosa;
    matrix[2][1] = uz * uy * (1 - cosa) + ux * sina;

    matrix[0][2] = ux * uz * (1 - cosa) + uy * sina;
    matrix[1][2] = uy * uz * (1 - cosa) - ux * sina;
    matrix[2][2] = uz * uz * (1 - cosa) + cosa;

    matrix[3][3] = 1;

    newx = x * matrix[0][0] + y * matrix[0][1] + z * matrix[0][2];
    newy = x * matrix[1][0] + y * matrix[1][1] + z * matrix[1][2];
    newz = x * matrix[2][0] + y * matrix[2][1] + z * matrix[2][2];

    x = newx;
    y = newy;
    z = newz;
}
